﻿using OPCAutomation;
using Robot.DeviceTestTools.Entity.Model;
using System;
using System.Collections.Generic;

namespace Robot.DeviceTestTools.Helper
{
    /// <summary>
    /// OPCDA自动化硬件通信Helper
    /// </summary>
    public class OPCAutoHelper
    {
        #region 公共变量

        /// <summary>
        /// OPC 服务器实例
        /// </summary>
        private OPCServer Server { get; set; } = new OPCServer();

        /// <summary>
        /// 配置点位组集合
        /// </summary>
        private OPCGroups Groups { get; set; }

        /// <summary>
        /// 配置点位组
        /// </summary>
        private OPCGroup Group { get; set; }

        /// <summary>
        /// 配置点位组下的节点集合
        /// </summary>
        private OPCItems Items { get; set; }

        /// <summary>
        /// 配置点位组下的节点
        /// </summary>
        private OPCItem Item { get; set; }

        /// <summary>
        /// 服务器下所有节点数据
        /// </summary>
        private List<string> NodeData { get; set; }

        /// <summary>
        /// 服务器连接状态
        /// </summary>
        public bool ConnectedState { get; private set; }

        /// <summary>
        /// 服务器断开连接事件
        /// </summary>
        /// <param name="Reason"></param>
        /// <returns></returns>
        private delegate string ServerShutDown(string Reason);

        /// <summary>
        /// 服务器断开连接事件
        /// </summary>
        private event ServerShutDown OnServerShutDown;

        #endregion

        #region 获取本机可用的OPCDA服务器名称列表

        /// <summary>
        /// 获取本机可用的OPCDA服务器名称列表
        /// </summary>
        /// <returns>可用的服务器名称列表</returns>
        public List<string> GetOPCServerList()
        {
            List<string> result = new List<string>();
            try
            {
                object ServerList = Server.GetOPCServers("");
                foreach (var item in (Array)ServerList)
                {
                    result.Add(item.ToString());
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex);
            }
            return result;
        }

        #endregion

        #region 连接 KEPServer 服务器

        /// <summary>
        /// 连接 KEPServer 服务器
        /// </summary>
        /// <param name="ServerName">服务器名称</param>
        /// <param name="ServerIP">服务器IP</param>
        /// <returns>成功或失败，返回结果信息</returns>
        public (bool, string) ConnectServer(string ServerName = "Kepware.KEPServerEX.V6", string ServerIP = "127.0.0.1")
        {
            try
            {
                // 如果服务器为空时则重新初始化
                if (Server == null) { Server = new OPCServer(); }

                // 打开连接
                Server.Connect(ServerName, ServerIP);

                // 如果连接成功打开时
                if (Server.ServerState == (int)OPCServerState.OPCRunning)
                {
                    // 挂载服务器断开事件
                    Server.ServerShutDown += Server_ServerShutDown;

                    // 设置状态为已连接
                    ConnectedState = true;

                    // 初始化点位组数据
                    AddGroup("RobotGroup");

                    // 返回结果
                    return (true, $"{DateTime.Now:yyyy-MM-dd HH:mm:ss} KEPServer 服务器连接成功");
                }
                else
                {
                    // 设置状态为未连接
                    ConnectedState = false;

                    // 返回结果
                    return (false, $"{DateTime.Now:yyyy-MM-dd HH:mm:ss} KEPServer 服务器连接失败。状态码：{Server.ServerState}");
                }
            }
            catch (Exception ex)
            {
                // 返回结果
                return (false, $"{DateTime.Now:yyyy-MM-dd HH:mm:ss} 连接 KEPServer 服务器发生异常, 错误信息: {ex.Message}");
            }
        }

        /// <summary>
        /// 服务器异常断开连接事件
        /// </summary>
        /// <param name="Reason"></param>
        private void Server_ServerShutDown(string Reason)
        {
            // 重置点位列表数据
            NodeData = new List<string>();

            // 设置状态为未连接
            ConnectedState = false;

            OnServerShutDown?.Invoke(Reason);
        }

        #endregion

        #region 断开 KEPServer 服务器连接

        /// <summary>
        /// 断开 KEPServer 服务器连接
        /// </summary>
        public void DisconnectServer()
        {
            try
            {
                // 重置点位列表数据
                NodeData = new List<string>();

                // 设置状态为未连接
                ConnectedState = false;

                Server?.Disconnect();
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
        }

        #endregion

        #region 获取OPCDA服务器中所有的节点

        /// <summary>
        /// 获取OPCDA服务器中所有的节点
        /// 
        /// 有一些节点服务器自带有Write的，不能读取。
        /// </summary>
        /// <returns>节点名称List</returns>
        public List<string> GetBrowser()
        {
            try
            {
                List<string> result = new List<string>();
                OPCBrowser browser = Server.CreateBrowser();
                browser.ShowBranches();
                browser.ShowLeafs(true);
                foreach (var item in browser)
                {
                    result.Add(item.ToString());
                }
                return result;
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
                return new List<string>();
            }
        }

        #endregion

        #region 添加配置点位组数据

        /// <summary>
        /// 添加配置点位组数据
        /// </summary>
        /// <param name="GroupName">组名称</param>
        public void AddGroup(string GroupName)
        {
            try
            {
                // 初始化组信息
                Groups = Server.OPCGroups;                           // 拿到组集合
                Groups.DefaultGroupIsActive = true;               // 设置组集合默认为激活状态
                Groups.DefaultGroupDeadband = 0;               // 设置死区
                Groups.DefaultGroupUpdateRate = 200;         // 设置更新频率

                Group = Server.OPCGroups.Add(GroupName);
                Group.IsActive = true;
                Group.IsSubscribed = true;
                Group.UpdateRate = 250;
                Group.DataChange += Group_DataChange;

                // 初始化可用点位列表
                NodeData = GetBrowser();
                //NodeData = GetBrowser().Where(w => w.Contains("通道 1.设备 1.标记 2")).ToList();

                // 添加需订阅的项到Group中
                for (int i = 0; i < NodeData.Count; i++)
                {
                    Global.SaveNodeData.Add(i, new ValueModel() { ClientHandle = i, Tag = NodeData[i] });
                    Group.OPCItems.AddItem(NodeData[i], i);
                }

                Console.WriteLine();
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex);
            }
        }

        /// <summary>
        /// 数据变化之后推送事件
        /// </summary>
        /// <param name="TransactionID"></param>
        /// <param name="NumItems"></param>
        /// <param name="ClientHandles"></param>
        /// <param name="ItemValues">值</param>
        /// <param name="Quanlity">质量</param>
        /// <param name="TimeStamps">时间戳</param>
        private void Group_DataChange(int TransactionID, int NumItems, ref Array ClientHandles, ref Array ItemValues, ref Array Quanlity, ref Array TimeStamps)
        {
            //Console.ForegroundColor = ConsoleColor.Green;
            //Console.Clear();
            for (int i = 1; i <= NumItems; i++)
            {
                try
                {
                    int tmpClientHandle = Convert.ToInt32(ClientHandles.GetValue(i));

                    Global.SaveNodeData[tmpClientHandle].Time = (DateTime)TimeStamps.GetValue(i);
                    Global.SaveNodeData[tmpClientHandle].Value = ItemValues.GetValue(i).ToString();
                    Global.SaveNodeData[tmpClientHandle].Quanlity = Quanlity.GetValue(i).ToString();

                    //Console.WriteLine($"Time: {Global.SaveNodeData[tmpClientHandle].Time:yyyy-MM-dd HH:mm:ss}");
                    //Console.WriteLine($"ClientHandle: {tmpClientHandle}");
                    //Console.WriteLine($"Tag: {Global.SaveNodeData[tmpClientHandle].Tag}");
                    //Console.WriteLine($"Value: {Global.SaveNodeData[tmpClientHandle].Value}");
                    //Console.WriteLine($"Qualities: {Global.SaveNodeData[tmpClientHandle].Quanlity}");
                }
                catch (Exception) { }
            }
        }

        #endregion

        #region 读取节点数据

        /// <summary>
        /// 读取单个节点数据
        /// </summary>
        /// <param name="tagAddress">需要读取的节点</param>
        /// <returns></returns>
        public ReadResultModel Read(string tagAddress)
        {
            try
            {
                OPCItem[] items = new OPCItem[1];
                items[0] = Items.AddItem(tagAddress, 0);
                items[0].Read(1, out object ItemValues, out object Qualities, out object TimeStamps);
                ReadResultModel result = new ReadResultModel()
                {
                    Tag = tagAddress,
                    Value = ItemValues.ToString(),
                    Quanlity = Qualities.ToString(),
                    Time = TimeStamps.ToString()
                };
                return result;
            }
            catch
            {
                return null;
            }
        }

        /// <summary>
        /// 读取多个节点数据
        /// </summary>
        /// <param name="tagAddress">需要读取的节点</param>
        /// <returns></returns>
        public List<ReadResultModel> Read(List<string> tagAddress)
        {
            List<ReadResultModel> results = new List<ReadResultModel>();
            try
            {
                OPCItem[] items = new OPCItem[tagAddress.Count];
                int[] temp = new int[items.Length + 1];
                temp[0] = 0;    // 这个第一个数据一定不能为空，索引是从1开始的
                for (int i = 0; i < items.Length; i++)
                {
                    try
                    {
                        items[i] = Items.AddItem(tagAddress[i], i);
                        items[i].Read(1, out object ItemValues, out object Qualities, out object TimeStamps);
                        ReadResultModel read_ = new ReadResultModel()
                        {
                            Tag = tagAddress[i],
                            Value = ItemValues.ToString(),
                            Quanlity = Qualities.ToString(),
                            Time = TimeStamps.ToString()
                        };
                        results.Add(read_);
                        temp[i + 1] = items[i].ServerHandle;
                    }
                    catch (Exception ex)
                    {
                        Console.WriteLine($"{tagAddress[i]} 发生异常: {ex}");
                    }
                }
                Array serverHandles = temp;
                Items.Remove(1, serverHandles, out Array errors);
                return results;
            }
            catch (Exception)
            {
                return null;
            }
        }

        #endregion

        #region 写入节点数据

        /// <summary>
        /// 写入节点数据
        /// </summary>
        /// <param name="tagAddress">节点地址</param>
        /// <param name="value">写入值</param>
        /// <returns></returns>
        public bool Write(string tagAddress, object value)
        {
            try
            {
                OPCItem items = Group.OPCItems.Item(tagAddress);
                items.Write(value);
                return true;
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex);
                return false;
            }
        }

        #endregion

    }
}
